﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//结构体

//结构体的声明 --- 例如描述一个学生
//struct stu
//{
//	char name[20];
//	char sex[5];
//	int age;
//	char id[12];
//};

//结构体类型的创建和初始化
//struct stu
//{
//	char name[20];
//	char sex[5];
//	int age;
//	char id[12];
//}s2,s3;//全局变量
//
//struct stu s4;//全局变量
//
//int main()
//{
//	//按照结构体顺序初始化
//	struct stu s = { "李沐","男",22,"2010330203" };//局部变量
//	printf("%s %s %d %s\n", s.name, s.sex, s.age, s.id);
//
//	//按照指定顺序初始化
//	struct stu s1 = { .age = 23,.name = "李木",.id = "2010330237",.sex = "女" };
//	printf("%s %s %d %s", s1.name, s1.sex, s1.age, s1.id);
//
//	return 0;
//}


//匿名的结构体类型，如果没有对结构体类型重命名的话，基本上只能使⽤⼀次。
//struct
//{
//	int a;
//	char b;
//	float c;
//}x;
//
//struct
//{
//	int a;
//	char b;
//	float c;
//}*pst;
//
//p = &x;//编译器会把上⾯的两个声明当成完全不同的两个类型，所以是⾮法的。
//
//int main()
//{
//
//	return 0;
//}


//结构的⾃引⽤
//struct Node
//{
//	int data;
//	struct Node* next;
//};

//结构体类型重命名
//typedef struct Node
//{
//	int data;
//	struct Node* next;
//}Node;



//结构体内存对⻬

//对⻬规则
//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
//2. 其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
//对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
//- VS 中默认的值为 8
//- Linux中 gcc 没有默认对⻬数，对⻬数就是成员⾃⾝的⼤⼩
//3. 结构体总⼤⼩为最⼤对⻬数（结构体中每个成员变量都有⼀个对⻬数，所有对⻬数中最⼤的）的整数倍。
//4. 如果嵌套了结构体的情况，嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处，结构
//   体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体中成员的对⻬数）的整数倍。


//练习1

//// 12
//struct S1
//{
//	char c1;
//	int i;
//	char c2;
//};

//练习2

//// 8
//struct S2
//{
//	char c1;
//	char c2;
//	int i;
//};

//练习3

//// 16
//struct S3
//{
//	double d;
//	char c;
//	int i;
//};
//
////练习4-结构体嵌套问题
//
//// 32
//struct S4
//{
//	char c1;
//	struct S3 s3;
//	double d;
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(struct S4));
//	return 0;
//}



//结构体存在内存对⻬?
//1. 平台原因(移植原因)
//2. 性能原因 --- 结构体的内存对⻬是拿空间来换取时间的做法。




//修改默认对⻬数
//#pragma pack(1) //修改对齐数为1
//struct s1
//{
//	char c1;
//	int i;
//	char c2;
//};
//#pragma pack() //取消对默认对齐数的修改
//
//int main()
//{
//	printf("%zd\n", sizeof(struct s1));
//
//	return 0;
//}


//结构体传参
//struct S
//{
//	int data[1000];
//	int num;
//};
//
//struct S s = { {1,2,3,4,5},7 };
//
//void print1(struct S s)
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//		printf("%d ", s.data[i]);
//	printf("\n%d\n", s.num);
//}
//
//void print2(struct S* pst)
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//		printf("%d ", pst->data[i]);
//	printf("\n%d\n", pst->num);
//
//}
//
//int main()
//{
//	print1(s);//传结构体 --- 传递⼀个结构体对象的时候，结构体过⼤，参数压栈的的系统开销⽐较⼤，所以会导致性能的下降
//
//	print2(&s);//传地址
//
//	return 0;
//}


//结构体实现位段

//位段的声明和结构是类似的，有两个不同：
//1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以
//选择其他类型。
//2. 位段的成员名后边有⼀个冒号和⼀个数字。

//struct A
//{
//	int _a : 2;
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(struct A)); // 8
//
//	return 0;
//}




//⼀个例⼦
//struct S
//{
//	char a : 3;
//	char b : 4;
//	char c : 5;
//	char d : 4;
//};
//
//int main()
//{
//	struct S s = { 0 };
//	s.a = 10;
//	s.b = 12;
//	s.c = 3;
//	s.d = 4;
//
//	printf("%zd\n", sizeof(struct S));
//
//	return 0;
//}




//位段的跨平台问题 --- 跟结构相⽐，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台的问题存在



//位段使⽤的注意事项
//不能对位段的成员使⽤& 操作符，这样就不能使⽤scanf直接给位段的成员输⼊值，只能是先输⼊
//放在⼀个变量中，然后赋值给位段的成员
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	struct A sa = { 0 };
	//scanf("%d", &sa._b);//这是错误的

	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa._b = b;
	return 0;
}